package com.dp.client.views.simulacion;

import java.util.ArrayList;
import java.util.List;


import com.dp.client.dto.AlmacenDTO;
import com.dp.client.dto.AlmacenSimulationDTO;
import com.dp.client.dto.ConexionDTO;
import com.dp.client.dto.ConexionSimulationDTO;
import com.dp.client.dto.Fecha;
import com.dp.client.dto.ResultadosQuerySimulacionDTO;
import com.dp.client.servicios.almacenes.IServiciosAlmacen;
import com.dp.client.servicios.almacenes.IServiciosAlmacenAsync;
import com.dp.client.servicios.conexion.IServiciosConexion;
import com.dp.client.servicios.conexion.IServiciosConexionAsync;
import com.dp.client.servicios.simulacion.IServiciosSimulacion;
import com.dp.client.servicios.simulacion.IServiciosSimulacionAsync;
import com.dp.client.util.Alerts;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.maps.client.MapOptions;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.base.LatLng;
import com.google.gwt.maps.client.events.click.ClickMapEvent;
import com.google.gwt.maps.client.events.click.ClickMapHandler;
import com.google.gwt.maps.client.mvc.MVCArray;
import com.google.gwt.maps.client.overlays.Circle;
import com.google.gwt.maps.client.overlays.CircleOptions;
import com.google.gwt.maps.client.overlays.InfoWindow;
import com.google.gwt.maps.client.overlays.InfoWindowOptions;
import com.google.gwt.maps.client.overlays.Marker;
import com.google.gwt.maps.client.overlays.MarkerOptions;
import com.google.gwt.maps.client.overlays.Polygon;
import com.google.gwt.maps.client.overlays.Polyline;
import com.google.gwt.maps.client.overlays.PolylineOptions;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.IntegerBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.PushButton;

public class SimulationPrincipal extends Composite {
	private boolean pintaLineas = false;
	private VerticalPanel verticalPanel;
	private Button btnCrearSimulacion;
	private FlexTable table;
	private IntegerBox txtMinutos;
	private int sesionID = -1;
	private SimplePanel panelMap;
	private FlexTable flexTable;
	private Label lblSeleccioneLaDimension;
	private IntegerBox diasPasoBox;
	private Label lblDiaActual;
	private IntegerBox diaActualBox;
	private Button btnIrASiguiente;
	private Label lblI;
	private IntegerBox txtDias;
	private Label lblIdDeSimulacion;
	private IntegerBox idSimulacionBox;
	private Label lblSeleccioneUnAlmacen;
	private ListBox comboAlmacenes;
	private Label lblNuevaCapacidad;
	private IntegerBox capacidadAlmacen;
	private Button btnAceptar;
	private Label lblSeleccioneUnaConexion;
	private ListBox comboConexiones;
	private Label label_1;
	private IntegerBox capacidadConexion;
	private Button button;
	private Button btnIniciarSimulacion;
	private MapWidget mapa;
	
	public SimulationPrincipal(boolean pintaMapa) {
		verticalPanel = new VerticalPanel();
		verticalPanel.setStyleName("admin");
		initWidget(verticalPanel);
		
		verticalPanel.setSize("700px", "100px");
		table = new FlexTable();
		
		table.setText(0, 0, "Ingrese el numero de minutos que desee que la sesion este activa");
		
		txtMinutos = new IntegerBox();
		txtMinutos.setText("10");
		table.setWidget(0, 1, txtMinutos);
		
		lblI = new Label("Ingrese el numero de dias que desea simular (maximo 360)");
		table.setWidget(1, 0, lblI);
		
		txtDias = new IntegerBox();
		txtDias.setText("100");
		table.setWidget(1, 1, txtDias);
		
		btnCrearSimulacion = new Button("Crear nueva simulacion");
		table.setWidget(2, 0, btnCrearSimulacion);
		btnCrearSimulacion.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				IServiciosSimulacionAsync servSimul = GWT.create(IServiciosSimulacion.class);
				txtDias.setValue(Math.min(txtDias.getValue(), 360));
				servSimul.crearSesionSimulacion(txtMinutos.getValue(), txtDias.getValue(), new AsyncCallback<Integer>(){
					@Override
					public void onFailure(Throwable caught) {
						Alerts.dialogErrorConexion();
					}
					@Override
					public void onSuccess(Integer result) {
						sesionID = result;
						idSimulacionBox.setValue(sesionID);
						Alerts.crearMensajeExitoCreacion("Simulacion");
					}

		});
			}
		});
		
		verticalPanel.add(table);
		
		lblIdDeSimulacion = new Label("Id de simulacion");
		table.setWidget(3, 0, lblIdDeSimulacion);
		
		idSimulacionBox = new IntegerBox();
		table.setWidget(3, 1, idSimulacionBox);
		
		lblUsarLaSiguiente = new Label("Usar la siguiente simulacion:");
		table.setWidget(3, 2, lblUsarLaSiguiente);
		
		idHardcode = new IntegerBox();
		table.setWidget(3, 3, idHardcode);
		
		btnAceptar_1 = new Button("Aceptar");
		btnAceptar_1.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				int valor = -1;
				if(!idHardcode.getText().isEmpty()) valor = idHardcode.getValue();
				if(valor < 0) Alerts.crearAlerta("Seleccione un valor no negativo");
				else{
					sesionID = valor;
					idSimulacionBox.setValue(sesionID);
				}
			}
		});
		table.setWidget(3, 4, btnAceptar_1);
		
		lblSeleccioneUnAlmacen = new Label("Seleccione un almacen para modificar su capacidad");
		table.setWidget(4, 0, lblSeleccioneUnAlmacen);
		
		comboAlmacenes = new ListBox();
		table.setWidget(4, 1, comboAlmacenes);
		
		lblNuevaCapacidad = new Label("Nueva capacidad");
		table.setWidget(4, 2, lblNuevaCapacidad);
		
		capacidadAlmacen = new IntegerBox();
		capacidadAlmacen.setText("200");
		table.setWidget(4, 3, capacidadAlmacen);
		
		btnAceptar = new Button("Aceptar");
		btnAceptar.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				IServiciosSimulacionAsync servSim = GWT.create(IServiciosSimulacion.class);
				servSim.cambioCapacidadAlmacen(sesionID, Integer.parseInt(comboAlmacenes.getValue(comboAlmacenes.getSelectedIndex())), capacidadAlmacen.getValue(), new AsyncCallback<Void>() {

					@Override
					public void onFailure(Throwable caught) {
						Alerts.dialogErrorConexion();
					}

					@Override
					public void onSuccess(Void result) {
						Alerts.confirmacionGenerica("Se modifico' satisfactoriamente la capacidad del amacen");
					}
				});
			}
		});
		table.setWidget(4, 4, btnAceptar);
		
		lblSeleccioneUnaConexion = new Label("Seleccione una conexion para modificar su capacidad");
		table.setWidget(5, 0, lblSeleccioneUnaConexion);
		
		comboConexiones = new ListBox();
		table.setWidget(5, 1, comboConexiones);
		
		label_1 = new Label("Nueva capacidad");
		table.setWidget(5, 2, label_1);
		
		capacidadConexion = new IntegerBox();
		capacidadConexion.setText("200");
		table.setWidget(5, 3, capacidadConexion);
		
		button = new Button("Aceptar");
		button.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				IServiciosSimulacionAsync servSim = GWT.create(IServiciosSimulacion.class);
				servSim.cambioCapacidadConexion(sesionID, Integer.parseInt(comboConexiones.getValue(comboConexiones.getSelectedIndex())), capacidadConexion.getValue(), new AsyncCallback<Void>() {

					@Override
					public void onFailure(Throwable caught) {
						Alerts.dialogErrorConexion();
					}

					@Override
					public void onSuccess(Void result) {
						Alerts.confirmacionGenerica("Se modifico' satisfactoriamente la capacidad de la conexion.");
					}
				});
			}
		});
		table.setWidget(5, 4, button);
		
		btnIniciarSimulacion = new Button("Iniciar Simulacion");
		btnIniciarSimulacion.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				IServiciosSimulacionAsync servAsync = GWT.create(IServiciosSimulacion.class);
				servAsync.startSimulacion(idSimulacionBox.getValue(), new AsyncCallback<Boolean>() {

					@Override
					public void onFailure(Throwable caught) {
						Alerts.dialogErrorConexion();
					}

					@Override
					public void onSuccess(Boolean result) {
						if(result)Alerts.dialogErrorPersonalizado("Se efectuo' completamente la simulacion con los siguientes datos:\n" + result);
						else Alerts.dialogErrorPersonalizado("Hubo un error en el simulador");
					}
				} );
			}
		});
		table.setWidget(6, 0, btnIniciarSimulacion);
		
		btnLineas = new Button("Mostrar conexxiones");
		btnLineas.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				pintaLineas = !pintaLineas;
				if(btnLineas.getText().equals("Mostrar conexiones")) 
					btnLineas.setText("No mostrar conexiones");
				else btnLineas.setText("Mostrar conexiones");
			}
		});
		btnLineas.setHTML("Mostrar conexiones");
		table.setWidget(6, 1, btnLineas);
		
		panelMap = new SimplePanel();
		verticalPanel.add(panelMap);
		
		flexTable = new FlexTable();
		verticalPanel.add(flexTable);
		
		lblSeleccioneLaDimension = new Label("Seleccione en numero de dias de cada paso");
		flexTable.setWidget(0, 0, lblSeleccioneLaDimension);
		
		diasPasoBox = new IntegerBox();
		diasPasoBox.setText("30");
		flexTable.setWidget(0, 1, diasPasoBox);
		
		lblDiaActual = new Label("Dia actual (desde 0)");
		flexTable.setWidget(1, 0, lblDiaActual);
		
		diaActualBox = new IntegerBox();
		diaActualBox.setText("0");
		flexTable.setWidget(1, 1, diaActualBox);
		
		btnIrASiguiente = new Button("Mostrar siguiente intervalo de tiempo");
		btnIrASiguiente.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				final int diaPaso = diasPasoBox.getValue();
				final int diaInicial = diaActualBox.getValue();
				IServiciosSimulacionAsync serv = GWT.create(IServiciosSimulacion.class);
				serv.getResultadosDeSimulacionParaIntervaloDeTiempo(sesionID, new Fecha(diaInicial, 0), new Fecha(diaPaso + diaInicial - 1, 0), new AsyncCallback<ResultadosQuerySimulacionDTO>() {
					
					@Override
					public void onSuccess(ResultadosQuerySimulacionDTO result) {
						pintaDatosMapa(result.getAlmacenes(), result.getConexiones());
						diaActualBox.setValue(diaInicial + diaPaso);
					}
					
					@Override
					public void onFailure(Throwable caught) {
						Alerts.dialogErrorConexion();
					}
				});
			}
		});
		
		lblSimulacionAutomatica = new Label("Simulacion automatica");
		flexTable.setWidget(2, 0, lblSimulacionAutomatica);
		
		lblMilisegundosPorPaso = new Label("Milisegundos por paso:");
		flexTable.setWidget(2, 1, lblMilisegundosPorPaso);
		
		integerBox = new IntegerBox();
		integerBox.setValue(500);
		flexTable.setWidget(2, 2, integerBox);
		
		btnAvanzar = new Button("Avanzar");
		flexTable.setWidget(2, 3, btnAvanzar);
		btnAvanzar.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				diaActualBox.setValue(0);
				final int diaPaso = diasPasoBox.getValue();
				Timer t = new Timer(){
					int diaInicial = 0;
					@Override
					public void run() {
						diaInicial += diaPaso;
						IServiciosSimulacionAsync serv = GWT.create(IServiciosSimulacion.class);
						serv.getResultadosDeSimulacionParaIntervaloDeTiempo(sesionID, new Fecha(diaInicial, 0), new Fecha(diaPaso + diaInicial - 1, 0), new AsyncCallback<ResultadosQuerySimulacionDTO>() {
							
							@Override
							public void onSuccess(ResultadosQuerySimulacionDTO result) {
								pintaDatosMapa(result.getAlmacenes(), result.getConexiones());
								diaActualBox.setValue(diaInicial + diaPaso);
							}
							
							@Override
							public void onFailure(Throwable caught) {
								Alerts.dialogErrorConexion();
							}
						});
						if(diaInicial > 360) cancel();
					}
					
				};
				if(!integerBox.getText().isEmpty() || integerBox.getValue() == 0 || integerBox.getValue() > 10000) integerBox.setValue(500);
				int step = integerBox.getValue();
				t.scheduleRepeating(step);
			}
		});
		btnIrASiguiente.setText("Mostrar analisis de siguiente intervalo de tiempo");
		flexTable.setWidget(3, 0, btnIrASiguiente);
		
		btnLimpiarMapa = new Button("Limpiar mapa");
		btnLimpiarMapa.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				limpiaMapa();
			}
		});
		flexTable.setWidget(3, 1, btnLimpiarMapa);
	
		if(pintaMapa) crearMapa();
		cargarDatos();
	}
	
	private void cargarDatos(){
		IServiciosAlmacenAsync servAlm = GWT.create(IServiciosAlmacen.class);
		servAlm.listAlmacenes(new AsyncCallback<List<AlmacenDTO>>() {
			
			@Override
			public void onSuccess(List<AlmacenDTO> result) {
				for(AlmacenDTO a : result){
					comboAlmacenes.addItem(a.getDescripcion(), a.getId() + "");
				}
			}
			
			@Override
			public void onFailure(Throwable caught) {
				Alerts.dialogErrorConexion();
			}
		});
		IServiciosConexionAsync servCon = GWT.create(IServiciosConexion.class);
		servCon.getConexiones("", "", "", "", "", "", "1111111", new AsyncCallback<List<ConexionDTO>>() {
			
			@Override
			public void onSuccess(List<ConexionDTO> result) {
				for(ConexionDTO c : result){
					comboConexiones.addItem(c.descripcion, c.id + "");
				}
			}
			
			@Override
			public void onFailure(Throwable caught) {
				Alerts.dialogErrorConexion();
			}
		});
	}

	private void crearMapa() {
		MapOptions opcion = MapOptions.newInstance();
		opcion.setZoom(3);
		LatLng center = LatLng.newInstance(-12.068909,-77.075529);
		opcion.setCenter(center);
		mapa = new MapWidget(opcion);
		mapa.setHeight("600px");
		mapa.setWidth("800px");
		
		panelMap.add(mapa);	
	}
	
	private List<Marker> markers = new ArrayList<Marker>();
	private List<Circle> circulos = new ArrayList<Circle>();
	private List<Polyline> lineas = new ArrayList<Polyline>();
	private Button btnLimpiarMapa;
	private Label lblUsarLaSiguiente;
	private IntegerBox idHardcode;
	private Button btnAceptar_1;
	private Label lblSimulacionAutomatica;
	private Button btnAvanzar;
	private Label lblMilisegundosPorPaso;
	private IntegerBox integerBox;
	private Button btnLineas;
	
	private void limpiaMapa(){
		for(Marker m : markers) m.close();
		for(Circle c : circulos){
			c.setMap(null);
			c.setRadius(0);
		}
		for(Polyline p : lineas) p.setMap(null);
		
		markers.clear();
		circulos.clear();
		lineas.clear();
	}
	private void pintaDatosMapa(List<AlmacenSimulationDTO> almacenes, List<ConexionSimulationDTO> conexiones){
		limpiaMapa();
		
		for(AlmacenSimulationDTO a : almacenes){
			addMarker(a.getDescripcion(), a.getLatitud(), a.getLongitud(), a.getCapacidadLlenada(), a.getCapacidadTotal(), a.getFechaBottle());
			addCircle(a.getFechaBottle(), a.getLatitud(), a.getLongitud(), a.getCapacidadLlenada(), a.getCapacidadTotal());
		}
		if(pintaLineas)
			for(ConexionSimulationDTO c : conexiones){
			AlmacenSimulationDTO a1 = buscaAlmacen(almacenes, c.getIdOrigen());
			AlmacenSimulationDTO a2 = buscaAlmacen(almacenes, c.getIdDestino());
			addLine(a1.getLatitud(), a1.getLongitud(), a2.getLatitud(), a2.getLongitud(), c.getCapacidadLlena(), c.getCapacidadTotal(), c.getFechaBottle());
		}
	}

	private void addLine(double latitud, double longitud, double latitud2,
			double longitud2, int capacidadLlena, int capacidadTotal,
			Fecha fechaBottle) {
		MVCArray<LatLng> path = MVCArray.newInstance();
		path.push(LatLng.newInstance(latitud, longitud));
		path.push(LatLng.newInstance(latitud2, longitud2));
		PolylineOptions opt = PolylineOptions.newInstance();
		opt.setPath(path);
		opt.setStrokeWeight(4);
		opt.setStrokeColor(getColorFromState(capacidadLlena, capacidadTotal));
		opt.setMap(mapa);
		Polyline line = Polyline.newInstance(opt);
		lineas.add(line);
	}

	private void addCircle(Fecha fechaBottle, double latitud, double longitud,
			int capacidadLlenada, int capacidadTotal) {
		CircleOptions opt = CircleOptions.newInstance();
		opt.setCenter(LatLng.newInstance(latitud, longitud));
		opt.setRadius(300000);
		opt.setFillColor(getColorFromState(capacidadLlenada, capacidadTotal));
		opt.setStrokeColor(getColorFromState(capacidadLlenada, capacidadTotal));
		opt.setStrokeWeight(0);
		opt.setMap(mapa);
		Circle c = Circle.newInstance(opt);
		circulos.add(c);
	}

	private String getColorFromState(int capacidadLlenada, int capacidadTotal) {
		if(capacidadTotal == 0) return "black";
		double ratio = ((double)capacidadLlenada) / capacidadTotal;
		if(ratio < 0.16) return "#00FF00";
		else if(ratio < 0.32) return "#33cc00";
		else if(ratio < 0.48) return "#669900";
		else if(ratio < 0.64) return "#996600";
		else if(ratio < 0.8) return "#cc3300";
		else return "#FF0000";
	}

	protected void drawInfoWindow(LatLng position, String mensaje) {
	    HTML html = new HTML(mensaje); 
	    InfoWindowOptions options = InfoWindowOptions.newInstance();
	    options.setContent(html);
	    InfoWindow iw = InfoWindow.newInstance(options);
	    iw.setPosition(position);
	    iw.open(mapa);
	  }
	private void addMarker(final Object descripcion, double latitud, double longitud, final int capacidadLlenada, final int capacidadTotal, final Fecha fecha){
		MarkerOptions opt = MarkerOptions.newInstance();
		opt.setPosition(LatLng.newInstance(latitud, longitud));
		opt.setMap(mapa);
		opt.setTitle(descripcion.toString());
		Marker marker = Marker.newInstance(opt);
		markers.add(marker);
		marker.addClickHandler(new ClickMapHandler() {
			
			@Override
			public void onEvent(ClickMapEvent event) {
				String mensaje = descripcion.toString() + ":";
				mensaje += "<br/><br/>Capacidad Total: " + capacidadTotal;
				mensaje += "<br/>Capacidad llenada: " + capacidadLlenada;
				mensaje += "<br/>Dia ocurrencia: " + fecha.getDia();
				mensaje += "<br/>Hora ocurrencia: " + fecha.getHora();
				drawInfoWindow(event.getMouseEvent().getLatLng(), mensaje);
			}
		});
	}
	private AlmacenSimulationDTO buscaAlmacen(
			List<AlmacenSimulationDTO> almacenes, int idOrigen) {
		for(AlmacenSimulationDTO a : almacenes) if(a.getId() == idOrigen) return a;
		return null;
	}
	

}
